/*================================================================
 *   Copyright (C) 2021 All rights reserved.
 *   
 *   文件名称：scheduler.cc.c
 *   创 建 者：汉森伯逸
 *   创建日期：2021/3/21
 *   描    述：
 *
 *================================================================*/

#include "scheduler.h"

#include "log.h"
#include "hook.h"

namespace hsby {

/**
 * @brief 调度器
 */
static thread_local Scheduler* t_scheduler = nullptr;

/**
 * @brief 工作协程
 */
static thread_local Fiber::Ptr t_working_fiber = nullptr;

/**
* @brief 构造函数
*/
Scheduler::Scheduler(size_t worker_count, const std::string& name)
        : name_(name),
          worker_count_(worker_count){
    HSBY_ASSERT(worker_count > 0);
    t_scheduler = this;
}

/**
 * @brief 虚析构函数
 */
Scheduler::~Scheduler() {
    if (GetCurrentScheduler() == this) {
        SetCurrentScheduler(nullptr);
    }
}

void Scheduler::Dump(std::ostream &os) const {
    os << "Scheduler{" << HSBY_VARS_EXP(name_, worker_count_) << "}";
}

bool Scheduler::Init() {
    MutexType::WriteLockGuardType lock(mutex_);
    // 安装工作线程
    for (size_t index = 0; index < worker_count_; ++index) {
        auto thread = std::shared_ptr<Thread>(new Thread(std::bind(&Scheduler::OnWorking, this), name_ + "_Worker_" + std::to_string(index)));
        HSBY_ASSERT(thread);
        workers_[thread->GetId()] = thread;
        worker_thread_ids.push_back(thread->GetId());
    }
    return true;
}

bool Scheduler::Start() {
    bool ret = Init();
    return ret;
}

/**
 * @brief 停止
 */
bool Scheduler::Stop() {
    is_stop = true;
    // 唤醒闲置工作线程
    if (idle_worker_count_ > 0) {
        Tickle();
    }
    // 回收工作线程
    for (auto it : workers_) {
        it.second->join();
    }
    return true;
}

void Scheduler::OnWorking() {
    HSBY_LOG_SYSTEM_INFO << *this << "::OnWorking() begin";
    set_hook_enable(true);
    Scheduler::SetCurrentScheduler(this);
    t_working_fiber = Fiber::GetCurrentFiber();

    Fiber::Ptr tmp_task_fiber = nullptr;
    Fiber::Ptr idle_fiber = std::shared_ptr<Fiber>(new Fiber(std::bind(&Scheduler::OnIdle, this), 0));
    while(/*!IsStopped()*/ true) {
        TimeInterval ti_working_loop;
        Task::Ptr task = nullptr;
        do {
            // 获取任务
            while (true) {
                // 任务出队
                TimeInterval ti1;
//                task = tasks_.Pull();
                task.reset();
                tasks_.try_dequeue(task);
                if (task) {
                    // 如果任务指定了线程Id， 则直接绕过
                    HSBY_LOG_SYSTEM_DEBUG << HSBY_VARS_EXP(task->thread_id, GetThisThreadId());
                    if (task->thread_id != -1 && task->thread_id != GetThisThreadId()) {
                        AddTask(task);
                        Tickle(task->thread_id);
                        continue;
                    }

                    // 如果任务在执行中
                    if (task ->fiber && task->fiber->GetState() == Fiber::EXEC) {
                        AddTask(task);
                        continue;
                    }
                }
                HSBY_LOG_SYSTEM_DEBUG << "GetTask" << ti1;
                break;
            }

            if (!task) {
                //HSBY_LOG_SYSTEM_DEBUG << "Pull task is nullptr";
                break;
            }

            // 如果任务是协程
            if (task->fiber && (task->fiber->GetState() != Fiber::TERM && task->fiber->GetState() != Fiber::EXCEPT)) {
                TimeInterval ti2;
                // 执行任务
                task->fiber->SwapIn();
                if (task->fiber->GetState() == Fiber::READY) {
                    AddTask(task);
                }else if (task->fiber->GetState() != Fiber::TERM
                          && task->fiber->GetState() != Fiber::EXCEPT) {
                    task->fiber->SetState(Fiber::HOLD);
                }
                HSBY_LOG_SYSTEM_DEBUG << "ExecTaskFiber" << ti2;

            // 如果任务是回调
            }else if (task->cb) {
                TimeInterval ti3;
                // 创建临时协程
                if (tmp_task_fiber) {
                    tmp_task_fiber->Reset(task->cb);
                }else {
                    tmp_task_fiber.reset(new Fiber(task->cb, 0));
                }

                // 执行任务
                tmp_task_fiber->SwapIn();

                if (tmp_task_fiber->GetState() == Fiber::READY) {
                    AddTask(tmp_task_fiber);
                    tmp_task_fiber.reset();
                }else if (tmp_task_fiber->GetState() == Fiber::EXCEPT
                          || tmp_task_fiber->GetState() == Fiber::TERM) {
                    tmp_task_fiber->Reset(nullptr);
                }else {
                    tmp_task_fiber->SetState(Fiber::HOLD);
                    tmp_task_fiber.reset();
                }
                HSBY_LOG_SYSTEM_DEBUG << "ExecTaskCb" << ti3;
            }

        }while(task);

        // 进入idle
        if (idle_fiber->GetState() == Fiber::TERM) {
            //HSBY_LOG_SYSTEM_DEBUG << GetThisThreadId() << " idle term";
            break;
        }

        idle_worker_count_++;
        active_worker_count_--;
        HSBY_LOG_SYSTEM_DEBUG << "WorkingLoop" << ti_working_loop;
        idle_fiber->SwapIn();
        active_worker_count_++;
        idle_worker_count_--;

        if (idle_fiber->GetState() != Fiber::TERM
            && idle_fiber->GetState() != Fiber::EXCEPT) {
            idle_fiber->SetState(Fiber::HOLD);
        }
    }

    HSBY_LOG_SYSTEM_INFO << *this << "::OnWorking() end";
}

void Scheduler::OnIdle() {
    while (!IsStopped()) {
        Fiber::YieldToHold();
    }
}

void Scheduler::Tickle(int thread_id) {
}

bool Scheduler::IsStopped() {
    return is_stop && active_worker_count_ == 0;
}

void Scheduler::AddTask(Task::Ptr task) {
    HSBY_LOG_SYSTEM_DEBUG << "Scheduler::AddTask()";
    tasks_.enqueue(task);
    Tickle();
}

/**
 * @brief 获取当前协程调度器
 */
Scheduler* Scheduler::GetCurrentScheduler() {
    return t_scheduler;
}

/**
 * @brief 设置当前协程调度器
 */
void Scheduler::SetCurrentScheduler(Scheduler* schedulerPtr) {
    t_scheduler = schedulerPtr;
}

/**
 * @brief 获取当前主协程
 */
Fiber::Ptr Scheduler::GetThreadWorkingFiber() {
    return t_working_fiber;
}

} // namespace hsby